package com.btpj.login_json.config;

import com.btpj.login_json.modell.RespBean;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.io.PrintWriter;

/**
 * Spring Security的配置类
 *
 * @author BTPJ  2020/8/7
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(10);
    }

    @Bean
    RoleHierarchy roleHierarchy() {
        // 配置admin继承所有的user接口权限
        RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
        hierarchy.setHierarchy("ROLE_admin > ROLE_user");
        return hierarchy;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //  super.configure(auth);
        auth.inMemoryAuthentication()
                // 内存中设置用户密码，多个用and相连 BTPJ/9191  LTP/1991
                .withUser("BTPJ").password("$2a$10$tN/uAwtMwTn00NB5c.arc.J8UaUKiGUm.emvGeoLbPWc7NIdd3XEG").roles("user")
                .and()
                .withUser("LTP").password("$2a$10$4LvAPMFMHKR4pdnzUO7IiuKcO.kJKgCO2P4GddGAP4nDXwamWKuB2").roles("admin");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/user/**").hasRole("user")
                .antMatchers("/admin/**").fullyAuthenticated() // 设置admin接口必须完整验证，记住密码登录方式不行
                .anyRequest().authenticated()
                .and()
                // 记住密码自动登录
                .rememberMe()
                .key("BTPJ")
                .and()
                .formLogin()
                .loginProcessingUrl("/doLogin") // 自定义登录提交URL
                .usernameParameter("userName") // 修改用户名提交参数
                .passwordParameter("pwd") // 修改用户名提交参数
                // 登录成功返回JSON
                .successHandler((httpServletRequest, httpServletResponse, authentication) -> {
                    Object principal = authentication.getPrincipal();
                    httpServletResponse.setContentType("application/json;charset=utf-8");
                    PrintWriter out = httpServletResponse.getWriter();
                    out.write(new ObjectMapper().writeValueAsString(RespBean.success(principal, "登录成功")));
                    out.flush();
                    out.close();
                })
                // 登录失败返回JSON
                .failureHandler((httpServletRequest, httpServletResponse, e) -> {
                    httpServletResponse.setContentType("application/json;charset=utf-8");
                    PrintWriter out = httpServletResponse.getWriter();
                    RespBean<Void> respBean = RespBean.error(e.getMessage());
                    if (e instanceof LockedException) {
                        respBean.setMsg("账户被锁定，请联系管理员!");
                    } else if (e instanceof CredentialsExpiredException) {
                        respBean.setMsg("密码过期，请联系管理员!");
                    } else if (e instanceof AccountExpiredException) {
                        respBean.setMsg("账户过期，请联系管理员!");
                    } else if (e instanceof DisabledException) {
                        respBean.setMsg("账户被禁用，请联系管理员!");
                    } else if (e instanceof BadCredentialsException) {
                        respBean.setMsg("用户名或者密码输入错误，请重新输入!");
                    }
                    out.write(new ObjectMapper().writeValueAsString(respBean));
                    out.flush();
                    out.close();
                })
                .permitAll()
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler((httpServletRequest, httpServletResponse, authentication) -> {
                    // 注销
                    httpServletResponse.setContentType("application/json;charset=utf-8");
                    PrintWriter out = httpServletResponse.getWriter();
                    out.write(new ObjectMapper().writeValueAsString(RespBean.success(null, "注销成功")));
                    out.flush();
                    out.close();
                })
                .permitAll()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint((httpServletRequest, httpServletResponse, e) -> {
                    // 未登录权限提示
                    httpServletResponse.setContentType("application/json;charset=utf-8");
                    PrintWriter out = httpServletResponse.getWriter();
                    out.write(new ObjectMapper().writeValueAsString(RespBean.error("请先登录")));
                    out.flush();
                    out.close();
                }).and()
                .csrf().disable()
                // 设置踢掉已登录用户
                .sessionManagement()
                .maximumSessions(1);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // static下的login静态资源去除权限拦截
//        web.ignoring().antMatchers("/login/**");
    }
}
